package Algorithms;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Random;
import java.util.Vector;

import runner.DateBD;

import Route.Legatura;
import Route.Statie;
import Stack.Stack;
import Stack.StackIterator;
import Templates.Operator;
import Templates.State;

public class UniformCostSearch<Template> {
	private Stack<State<Template>> btStack ;
	private ArrayList<Operator<Template>> operators;
	private State<Template> initialState;
	private ArrayList<State<Template>> finalStates;
	private int operatorNumber =0;
	private PrintWriter logWriter = null;
	private File log;
	private int expandCounter=0;
	private double [] h;
	private int k = 4;
	private int cost = 0;
	private PriorityQueue<Statie> queue = new PriorityQueue<Statie>();
	private PriorityQueue<Integer> costQueue = new PriorityQueue<Integer>();
	
	/**
	 * 
	 * @param iState stare initiala
	 * @param fStates stari finale
	 * @param opr operatori
	 * @param log fisierul in care afisam
	 */
	public UniformCostSearch(State<Template> iState,ArrayList<State<Template>> fStates,ArrayList<Operator<Template>> opr,String log)
	{
		//generateH();
		btStack=new Stack<State<Template>>(100);
		initialState=iState;
		finalStates=fStates;
		for(int i = 0;i<finalStates.size();i++)
			finalStates.get(i).setH(0);
		if(opr != null){
			operators=opr;
			operatorNumber=operators.size()-1;
		}
		try {
			logWriter = new PrintWriter(new FileWriter(log));
		} catch (IOException e) {
			e.printStackTrace();
		}
		this.log=new File(log);
	}
	
	/**
	 * 
	 * @param thisState starea ce o cautam in stiva
	 * @return
	 */
	private boolean found(State<Template> thisState)
	{
		for(StackIterator<State<Template>> it=new StackIterator<State<Template>>(btStack); it.hasNext();)
		{
			if(thisState.equals(it.next())) return true;
		}
		return false;
	}
	
	/**
	 * 
	 * @param thisState starea ce vrem sa o comparam cu starile finale
	 * @return true daca starea curenta este stare finala sau false in caz contrar
	 */
	private boolean solutionFound(State<Template> thisState)
	{
		for(int i=0; i<finalStates.size();i++)
			if (thisState.equals(finalStates.get(i)))
				return true;
		return false;
	}
	
	/**
	 * 
	 * @param initialValue indicele de inceput in stiva
	 * @param finalValue indicele pana la care parcurg stiva
	 * @return true daca am rezolvat problema si false altfel
	 */
	private boolean solve(int initialValue,int finalValue)
	{
		//expandCounter=0;
		for(int i=initialValue;i<=finalValue;i++)
		{
			if(expand(btStack.get(i))){ return true; }				
		}
		if(initialValue > finalValue)
			solve(finalValue+1,finalValue+k);
		return false;
	}
	
	/**
	 * 
	 * @param thisState starea ce trebuie expandata
	 * @return true daca unul dintre succesori este solutie si false altfel
	 */
	private boolean expand(State<Template> thisState)
	{
		for(int i=0;i<=operatorNumber;i++)
		{
			if (operators.get(i).available(thisState.getCurrentState())) 
				{
					State<Template> auxiliary=new State<Template>(operators.get(i).execute(thisState.getCurrentState()), operators.get(i));
					if (!found(auxiliary)) { 
						expandCounter++; btStack.push(auxiliary); 
						if (solutionFound(auxiliary)) return true;
					}
				}
		}
		
		return false;
	}
	
	private boolean expand(Statie state){
		int [] cost1 = new int[300];
		Statie [] end = new Statie[300];
		int k = 0;
		int l = 0;
		for(int i = 0;i<DateBD.cl.total();i++)
			if(((Legatura)(DateBD.cl.getContinut().get(i))).getStart().equals(state)) {
				cost1[k++] = (cost+((Legatura)(DateBD.cl.getContinut().get(i))).getDurata());
				end[k-1] = ((Legatura)(DateBD.cl.getContinut().get(i))).getEnd();
			}
		for(int i = 0;i<k-1;i++){
			int min = cost1[i];
			for(int j = i+1;j<k;j++)
				if(min>cost1[j])
					l = j;
			int aux = cost1[i];
			cost1[i] = cost1[l];
			cost1[l] = aux;
			Statie aux1 = end[i];
			end[l] = end[i];
			end[i] = aux1;
		}
		for(int i = 0;i<k;i++){
			queue.add(end[i]);
			costQueue.add(cost1[i]);
		}
		return false;
	}
	
	/**
	 * 
	 * @param thisState starea curenta pe care o scriem in fisier
	 */
	private void print(State<Template> thisState)
	{
		logWriter.append("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
		logWriter.append(thisState.getCurrentOperator().toString());
		logWriter.append('\n');
		logWriter.append(thisState.getCurrentState().toString());
		logWriter.append('\n');
	}
	
	private void closeLog()
	{
		System.out.println("Solution found using Indepth Search... Stacktrace can be found in: " + log.getAbsolutePath());
		logWriter.close();
	}
	
	public void executeAlgorithm()
	{
		btStack.push(initialState);
		solve(0,0);
		for(StackIterator<State<Template>> it=new StackIterator<State<Template>>(btStack); it.hasNext();)
		{
			print(it.next());
		}
		closeLog();
	}


}
